#include "FileSystem.h"







FileSystem::FileSystem()
{
	this->disk =new  MyDisk(-1,-1,-1,false);

}

FileSystem::FileSystem(int _diskSize, int _blockSize, int _numOfNodes)
{
	this->disk =new  MyDisk(_diskSize,_blockSize,_numOfNodes,true);

}


FileSystem::~FileSystem()
{
	delete(this->disk);
}



/****THE FILE SYSTEM FUNCTIONS************/

int FileSystem::createFile (char flag){

	int inode = disk->allocateInode();
//	printf("allocate node %d\n",inode);
	char* f = (char*)malloc(1);;
	f[0] = flag;
	disk->setInodeType(inode, f);
	free(f);
	return inode;
}

int FileSystem::createDir(){
	int inode = disk->allocateInode();
	char* d =(char*)malloc(1);
		d[0] = 'D';
	disk->setInodeType(inode, d);
	free(d);
	return inode;
}

char FileSystem::getFileType(int inode){
	return disk->getInodeType(inode);
}


int FileSystem::f_write(int inode , char* buffer , int offset , int nBytes){

        if (this->disk->isFreeInode(inode) ){
                return -1;
        }
        //////incase of a hole
        if (this->disk->getFileSize(inode) < offset){
 //               printf("trying to write in jump over place is not been writen yet\n");
                this->fillHole(inode,offset);
                return f_write(inode , buffer , offset , nBytes);
        }
        /////
        int blockIndex = offset/this->disk->SB.blockSize;
        int offsetInBlock = offset%this->disk->SB.blockSize;
        int numOfBytesInBlock = this->disk->SB.blockSize - offsetInBlock;
        int block;
        int writen;



        if (blockIndex > 9){//incase of indirect Block~~~~~~~~~~~~~~~*/
                int indirectBlock;

                if (blockIndex > this->disk->SB.blockSize/4 + 9){
                        printf("ERROR :trying to write to a place that is not existent offset = %d\n", offset);
                        return -1;
                }
                else {
                        if (this->isBlockAllocFile(inode,10)){//the indorect block;
                                indirectBlock = this->disk->getDataBlock(inode,10);

                        }
                        else if (blockIndex == 10){
                                indirectBlock = this->disk->allocateDataBlock();
                                this->disk->initIndirectBlock(indirectBlock);
                                this->disk->setDataBlock(inode,10,indirectBlock);

                        }
                        else{
                                printf("ERROR: shuldnt get here\n");
                                return -1;

                        }
                        block = this->disk->getInDirectDataBlock(inode,blockIndex - 10);
                        if (block == -1){
                                block = this->disk->allocateDataBlock();
                                this->disk->setInDirectDataBlock(inode,blockIndex - 10,block);

                        }
                        if (numOfBytesInBlock > nBytes){
                                return  this->writeAndSet(buffer , blockIndex , block , offsetInBlock , nBytes ,inode);

                        }
                        else{
                                if (this->writeAndSet(buffer , blockIndex , block , offsetInBlock , numOfBytesInBlock ,inode)
                                                 > 0){
                                        return  this->f_write(inode,
                                                        buffer + numOfBytesInBlock,
                                                        offset + numOfBytesInBlock ,
                                                        nBytes - numOfBytesInBlock);
                                }
                                else {
                                        return -1;
                                }
                        }
                }//else
        }//if block > 10
        //end of  indirect Block~~~~~~~~~~~~~~~*/
        //****************direct************************//
        if (this->isBlockAllocFile(inode,blockIndex)){
                block = this->disk->getDataBlock(inode,blockIndex);
        }
        else {
                block = this->disk->allocateDataBlock();
                this->disk->setDataBlock(inode,blockIndex,block);
        }
        if (numOfBytesInBlock > nBytes){
                return  this->writeAndSet(buffer , blockIndex , block , offsetInBlock , nBytes ,inode);
        }
        else{
                if (this->writeAndSet(buffer , blockIndex , block , offsetInBlock , numOfBytesInBlock ,inode) > 0){
                return  this->f_write(inode,
                                        buffer + numOfBytesInBlock,
                                        offset + numOfBytesInBlock ,
                                        nBytes - numOfBytesInBlock);
                }
                else {

                        return -1;
                }
        }
}

int  FileSystem::f_read(int inode , char* buffer , int offset , int nBytes){
//	if (this->disk->isFreeInode(inode) || nBytes == 0){
//		printf("Error:trying to read from inode not existent %d\n",inode);
//		return -1;
//	}
	int blockIndex = offset/this->disk->SB.blockSize;
	int offsetInBlock = offset%this->disk->SB.blockSize;
	int numOfBytesInBlock = this->disk->SB.blockSize - offsetInBlock;
	int block;

	if (blockIndex > 9){//incase of indirect Block~~~~~~~~~~~~~~~*/

		int indirectBlock;

		if (blockIndex > this->disk->SB.blockSize/4 + 9){
			printf("ERROR: trying to read from a place that is not existent offset = %d\n", offset);
		}
		else {
			if (this->isBlockAllocFile(inode,10)){//the indorect block;
				indirectBlock = this->disk->getDataBlock(inode,10);
			}
			else{
				printf("ERROR:trying to read from  place is not been written1 yet\n");
				return -1;
			}
			block = this->disk->getInDirectDataBlock(inode,blockIndex - 10);
			if (block == -1){
				printf("ERROR:trying to read from  place is not been written2 yet\n");
				return -1;
			}
			if (numOfBytesInBlock > nBytes){
				return this->disk->readChar(buffer,this->disk->blockToAddress(block)+offsetInBlock,nBytes);
			}
			else{
				if (this->disk->readChar(buffer,
					this->disk->blockToAddress(block)+offsetInBlock,
						numOfBytesInBlock) > -1){
					return	this->f_read(inode,
							buffer + numOfBytesInBlock,
							offset + numOfBytesInBlock ,
							nBytes - numOfBytesInBlock);
				}
				else {
					return -1;
				}
			}
		}//else
	}//if block > 10
	//end of  indirect Block~~~~~~~~~~~~~~~*/
	//****************direct************************//
		if (this->isBlockAllocFile(inode,blockIndex)){
			block = this->disk->getDataBlock(inode,blockIndex);
		}
		else {//this section has no data
			printf("Error: this section in file there is no data %d \n",blockIndex);
			return -1;
		}
		if (numOfBytesInBlock > nBytes){
			return this->disk->readChar(buffer,this->disk->blockToAddress(block)+offsetInBlock,nBytes);
		}
		else{
			if (this->disk->readChar(buffer,
					this->disk->blockToAddress(block)+offsetInBlock,
					numOfBytesInBlock) > -1){
					return	this->f_read(inode,
							buffer + numOfBytesInBlock,
							offset + numOfBytesInBlock ,
							nBytes - numOfBytesInBlock);
			}
			else {
				return -1;
			}
		}
	}


int FileSystem::d_write(int inode , list<pair<int,char*> >* dirList){
	this->disk->deleteInode(inode);
	if (dirList == 0){
		return 0;
	}
	this->disk->setFileSize(inode , 0);
	int lSize = dirList->size();
	char * str = (char*)malloc(lSize*16);
//	printf("the size of list before lsitTostring %d\n",lSize);
	this->listToString(dirList,str);
//	str[16] = '\0';
//	printf("inode %d string is %d\n[%s]\n",inode,strlen(str),str);
	this->f_write(inode,str,0,strlen(str));
//	printf("size of the file %d now is %d\n",inode,this->disk->getFileSize(inode));
	free(str);
	return 1;
}

list<pair<int,char*> >* FileSystem::d_read(int inode){
	int fileSize=this->disk->getFileSize(inode);
	char * str = (char*)malloc(fileSize);

	if (fileSize == 0 || this->f_read(inode,str,0,fileSize) < 0){
	//	printf("str less then 16 \n[%s]\n",str);
		free(str);
		return NULL;
	}
	list<pair<int,char*> >* ans = new list<pair<int,char*> >();
	str[fileSize] = '\0';
//	printf("string deliverd to string to list is %d\n[%s]\n",fileSize,str);
	this->stringToList(ans,str);
//	printf("size d_read is %d\n",ans->size());
	free(str);
	return ans;


}



int FileSystem::f_delete(int inode){
	return this->disk->freeInode(inode);
}

int FileSystem::d_delete(int inode){
	if (this->isEmptyDir(inode))
		return this->disk->freeInode(inode);
	return -1;
}



/*MY HELPING FUNCTIONS*/

MyDisk* FileSystem::getMyDisk(){
	return this->disk;
}

bool FileSystem::isEmptyDir(int inode){
	list<pair<int , char*> >* lis;
	lis = this->d_read(inode);
	if ( lis == NULL) return true;
	int size = lis->size();
 	if (size == 0){
		return true;
	}

	return false;
}

bool FileSystem::isBlockAllocFile(int inode,int index){
	if (this->disk->getDataBlock(inode,index) > 0){
		return true;
	}
	return false;
}

int FileSystem::writeAndSet(char * buffer,int blockIndex,int block ,int offsetInBlock ,int nBytes, int inode){
	int written;
	int tmp;
	int fileSize = this->disk->getFileSize(inode);
	written = this->disk->writeChar(buffer,this->disk->blockToAddress(block) + offsetInBlock , nBytes );
	tmp = this->disk->blockToAddress(blockIndex) + offsetInBlock + written;
	if (fileSize < tmp ){
		this->disk->setFileSize(inode ,tmp  );
	}
	return written;


}


int FileSystem::fillHole(int inode , int EndOfHole){
	int fileSize = this->disk->getFileSize(inode);
	int nBytes= EndOfHole -fileSize;
	char * buf = (char*)malloc(nBytes);
	int i = 0;
	while (i < nBytes){
		strncpy(buf+i,"0",1);
		i++;
	}
	int  ans =this->f_write(inode,buf,fileSize,nBytes);
	free(buf);
	return ans;
}



int FileSystem::listToString(list<pair<int,char*> >* lstDir, char* str){

	list<pair<int,char*> >::const_iterator it;
	int j=0;
	int i=0;
	str[0] = '\0';
	for ( it = lstDir->begin();	it != lstDir->end(); it++){
		 char * tmp1 =itoa(it->first);
		 		j =  strlen(tmp1);
		 		while (j < 3){
		 			this->strCopy(tmp1+j," ",1);
		 			j++;
		 		}
		 		this->strCopy( str+(i*16), tmp1,3 );
		 		char * tmp2 = it->second;
		 		strCopy( str+(i*16)+3 ,tmp2 , 13 );
		 		strCopy(str+((i+1)*16) - 1,"0",1);
		 		strCopy(str+((i+1)*16),"\0",1);
		 	//	str[(i+1)*16]='\0';
		 		i++;
		 	//	printf("[%s]\n",tmp2);
	 }

	return 0;
}

void FileSystem::stringToList(list<pair<int,char*> >* lstDir, char* str){


	int len =strlen(str);
//	printf("stringToList string %d \n[%s]\n",len,str);
	int j , i=0;
	for(i = 0 ; i < len  ; i = i + 16 ){

		char* num =(char*)malloc(3);

		strCopy(num ,str+i ,3);

		j = atoi(num);
		char * file=(char*)malloc(13);
		strCopy(file,str+i+3,13);
		strCopy(file+12,"\0",1);
//		printf("string to list read [%s]\n",file);
		if (memChar( file, '/', 13 ) < 0){
			printf("Notice:folder has zevel - not ending with / %s\n",file);
			break;
		}
		pair<int,char* > curPair;

		curPair.first = j;
		curPair.second = file;
		lstDir->push_back(curPair);
	}
}


char* FileSystem::itoa (int n){
	int i=0,j;
	char* s;
	char* u;
	s= (char*) malloc(17);
	u= (char*) malloc(17);
	do{
		s[i++]=(char)( n%10+48 );
		n-=n%10;
	}
	while((n/=10)>0);
	for (j=0;j<i;j++)
		u[i-1-j]=s[j];
	u[j]='\0';
	return u;
}

int FileSystem::makeValidName(char* name ){
	int i=0;
//	printf("name before valid %s\n",name);
	while ((47 < name[i] && name[i] < 57) || (64 < name[i] && name[i] < 91) ||
			(96 < name[i] && name[i] < 123)){
		i++;
	}

	strncpy(name+i,"/",1);
	while (i < 12){
		strncpy(name+i+1,"0",1);
		i++;
	}
	strncpy(name+i+1,"\0",1);
//	printf("name after valid %s\n",name);
	return 1;
}


int FileSystem::getInodeNumber(char * name,list<pair<int,char*> >* lstDir){
//	printf("Debug:getInodeNumber\n");
	list<pair<int,char*> >::const_iterator it;
	int j=0;
	int i=0;
	char * tmp2 = (char*)malloc(13);
	strCopy(tmp2 ,name ,12);
	strCopy(tmp2 +12 ,"\0",1 );
	if(this->makeValidName(tmp2) == -1) {
		printf("not valid\n");
		free(tmp2);
		return -1;
	}

	for ( it = lstDir->begin();	it != lstDir->end(); it++){
		char * tmp= (char*)malloc(13);
		strCopy(tmp,it->second,13);
		strCopy(tmp+12,"\0",1);
	//	printf("compare between {%s} [%s] ,[%s] 12\n",it->second,tmp ,tmp2);
		if (this->strComp( tmp, tmp2, 12 ) == 0){
			free(tmp);
			free(tmp2);
			return it->first;
		}
	}
	free(tmp2);
	return -2;
}

int FileSystem::strCopy(char* dest , char* source , int n){
	 	int i=0;
	 	while( i < n){
	 		dest[i] = source[i];
	 		i++;
	 	}
	 //	dest[n+1]='\0';
	 	return n;
	 }

int  FileSystem::memChar(char* str , char x,int n){
	 int i=0;
	 while (i < n){
		 if (str[i] == x ){
			 return i;
		 }
		 i++;
	 }
	 return -1;
}

int FileSystem::strComp(char* dest, char* source ,int n){
	int i=0;
	while (i < n){
		if (dest[i] != source[i])
			return -1;
		i++;
	}
	return 0;
}


/**PRINTING FUNCTIONS**/

void FileSystem::printFile(int inode){
	disk->printInode(inode);
	printf("printnig the data of the file\n");
	int size= this->disk->getFileSize(inode);
	char * buf = (char*)malloc(size+1);
	if (size == 0)
		printf("no data\n");
	else{
		this->f_read(inode,buf,0,size);
		buf[size]='\0';
		printf("%s\n",buf);
	}
	free(buf);
}


/*
void FileSystem::printDirectoryList(list<pair<int,char*> >* lstDir){

	int size = lstDir->size();
	printf("size of list is %d\n",size);
	char* buffer1=(char*)malloc(16*size);
	char* buffer2=(char*)malloc(16*size + size);
	this->listToString(lstDir,buffer1);
	int tmp1=0;
	int tmp2=0;

	for (int i=0; i < size ;i++){
		strncat(buffer2+tmp2,buffer1+tmp1,16);
		strncat(buffer2+tmp2+1,"\n",1);

		tmp1 =tmp1+16;
		tmp2=tmp2+17;
	}
	free(buffer1);

}
*/

